package com.mall.common.service.impl;

import static com.mall.common.Constants.REDIS_ARTICLE_PREFIX;
import static com.mall.common.Constants.REDIS_ARTICLE_TITLE;

import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.mall.b2c.util.SettingUtils;
import com.mall.common.bean.ArticleBean;
import com.mall.common.bean.Order;
import com.mall.common.dao.ArticleDao;
import com.mall.common.domain.content.ArticleDomain;
import com.mall.common.hibernate.Finder;
import com.mall.common.service.ArticleCategoryService;
import com.mall.common.service.ArticleService;

/**
 * Service实现类 - 文章类别
 */
@Service
@Transactional
public class ArticleServiceImpl extends BaseServiceImpl<ArticleDomain, Long> implements ArticleService {

	@Resource private ArticleDao articleDao;
	@Resource private ArticleCategoryService articleCategoryService;
	@Resource private RedisTemplate<String, String> redisTemplate;  
	@Resource private TaskExecutor taskExecutor;
	@Resource private JdbcTemplate jdbcTemplate;
	
	@Resource
	public void setBaseDao(ArticleDao articleDao) {
		super.setBaseDao(articleDao);
	}

	@Override
	public  List<ArticleDomain> findList(String categoryCode,
			Date beginDate, Date endDate){
		String hql = "from ArticleDomain bean where 1 = 1";
		Finder f = Finder.create(hql);
		if(StringUtils.isNotBlank(categoryCode)){
			f.append(" and bean.category.code like concat(:code,'%')");
			f.setParam("code", categoryCode);
		}
		if(beginDate!=null){
			f.append(" and bean.modifyDate >= :beginDate");
			f.setParam("beginDate",beginDate);
		}
		if(endDate!=null){
			f.append(" and bean.modifyDate <= :endDate");
			f.setParam("endDate",endDate);
		}
		return f.findHQLList();
	}
	
	private void cleanArticleCache(final NameValuePair[] params){
		taskExecutor.execute(new Runnable() {
			
			@Override
			public void run() {
				HttpClient httpClient = new HttpClient();
				GetMethod getMethod = new GetMethod(SettingUtils.get().getUrlBuff(true).append("/cleanArticleCache.jhtml").toString());
				getMethod.setQueryString(params);
				try {
					httpClient.executeMethod(getMethod);
				} catch (Exception e) {
					
				}
			}
		});
	}

	@Override
	@Transactional(readOnly=true)
	@Cacheable(value="article", key="'article'+#catId+','+#isPublication+','+#isRecommend+','+#isTop+','+#orderBy+','+#count+','+#categoryCode")
	public List<ArticleBean> getAticles(Long catId, Boolean isPublication,
			Boolean isRecommend, Boolean isTop, String orderBy , Integer count, String categoryCode) {
		String hql ="select new com.mall.common.bean.ArticleBean(a) from ArticleDomain a where 1 = 1";
		Finder finder = Finder.create(hql);
		if(catId != null){
			finder.append(" and a.category.id =:categoryId");
			finder.setParam("categoryId", catId);
		}
		if (isPublication != null) {
			finder.append(" and a.isPublication=:isPublication");
			finder.setParam("isPublication", isPublication);
		}
		if (isRecommend != null) {
			finder.append(" and a.isRecommend=:isRecommend");
			finder.setParam("isRecommend", isRecommend);
		}
		if (isTop != null) {
			finder.append(" and a.isTop=:isTop");
			finder.setParam("isTop", isTop);
		}
		if(StringUtils.isNotBlank(categoryCode)){
			finder.append(" and a.category.code=:code");
			finder.setParam("code", categoryCode);
		}
		if (orderBy!= null) {
			finder.setOrder(Order.desc(orderBy));
		}	
		if(count != null){
			finder.setMaxResults(count);
		}
		return finder.findHQLList();
	}


	@Override
	public List<ArticleDomain> getAticles(Long catId, Date beginDate, Date endDate) {
		return articleDao.findList(catId,beginDate,endDate);
	}

	@Override
	@CacheEvict(value="article",allEntries=true)
	@Transactional(propagation=Propagation.REQUIRED)
	public void pubish(Long[] ids) {
		Finder finder = Finder.create("from ArticleDomain bean where bean.id in (:ids)");
		finder.setParamList("ids", ids);
		List<ArticleDomain> articleList = finder.findHQLList();
		NameValuePair[] params = new NameValuePair[articleList.size()];
		for(int i = 0;i < articleList.size();i++){
			ArticleDomain article = articleList.get(i);
			article.setReleaseDate(new Date());
			article.setIsPublication(true);
			update(article);
			params[i] = new NameValuePair("pathes", article.getRelatePath());
		}
		cleanArticleCache(params);
	}

	@Override
	public void syncArticleHit(Integer topCount) {
		HashOperations<String, String, String> ho = redisTemplate.opsForHash();
		ZSetOperations<String, String> zso = redisTemplate.opsForZSet();
		Long count = zso.count(REDIS_ARTICLE_PREFIX, Double.MIN_VALUE, Double.MAX_VALUE);
		if(count == null || count == 0){
			taskExecutor.execute(new syncRunnable(jdbcTemplate, zso, ho, topCount));
		}
	}
	
	private class syncRunnable implements Runnable{
		
		private JdbcTemplate jdbcTemplate;
		private ZSetOperations<String, String> zso;
		private HashOperations<String, String, String> ho;
		private Integer topCount;
		
		public syncRunnable(JdbcTemplate jdbcTemplate,
				ZSetOperations<String, String> zso,
				HashOperations<String, String, String> ho,
				Integer topCount) {
			super();
			this.jdbcTemplate = jdbcTemplate;
			this.zso = zso;
			this.ho = ho;
			this.topCount = topCount;
		}

		@Override
		public void run() {
			String sql = "select code from b2b_article_category where code is not null";
			List<Map<String, Object>> cateList = jdbcTemplate.queryForList(sql);
			for(Map<String, Object> cate : cateList){
				sync(cate.get("code").toString(), topCount);
			}
			sync(null, topCount);
		}
		
		private void sync(String code, Integer topCount){
			Object[] params = null;
			String sql = null;
			if(StringUtils.isBlank(code)){
				sql = "select ba.id,ba.title,ba.views from b2b_article ba,b2b_article_category bac where ba.category_id = bac.id and ba.views > 0";
				code = "";
			}else{
				sql = "select ba.id,ba.title,ba.views from b2b_article ba,b2b_article_category bac where ba.category_id = bac.id and ba.views > 0 and bac.code like '" + code + "%'";
			}
			if(topCount != null){
				sql += " order by views desc limit ?";
				params = new Object[]{topCount};
			}else{
				params = new Object[0];
			}
			List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, params);
			for(Map<String, Object> item : list){
				Object obj = item.get("views");
				if(obj != null){
					zso.incrementScore(REDIS_ARTICLE_PREFIX + code, item.get("id").toString(), Double.parseDouble(obj.toString()));
				}
				ho.put(REDIS_ARTICLE_TITLE, item.get("id").toString(), item.get("title").toString());
			}
		}
		
	}

	@Override
	@CacheEvict(value="article",allEntries=true)
	@Transactional(propagation=Propagation.REQUIRED)
	public void persist(ArticleDomain entity) {
		super.persist(entity);
	}

	@Override
	@CacheEvict(value="article",allEntries=true)
	@Transactional(propagation=Propagation.REQUIRED)
	public void remove(Long[] ids) {
		List<ArticleDomain> articleList = get(ids);
		NameValuePair[] params = new NameValuePair[articleList.size()];
		for(int i = 0;i < articleList.size();i++){
			ArticleDomain article = articleList.get(i);
			params[i] = new NameValuePair("pathes", article.getRelatePath());
			remove(article);
		}
		cleanArticleCache(params);
	}

	@Override
	@CacheEvict(value="article",allEntries=true)
	@Transactional(propagation=Propagation.REQUIRED)
	public ArticleDomain updateByUpdater(ArticleDomain entity) {
		ArticleDomain article = find(entity.getId());
		if(article != null){
			NameValuePair[] params = {new NameValuePair("pathes", article.getRelatePath())};
			cleanArticleCache(params);
		}
		return super.updateByUpdater(entity);
	}

}